﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using SimpleModeler.Graphics;
using System.Drawing;
using SimpleModeler.GUI.ModelerEventArgs;
using System.Collections;

namespace SimpleModeler.GUI
{
    /// <summary>
    /// Stellt eine Toolbar zum bearbeiten der Elemente dar
    /// </summary>
    public class Toolbar : IDisposable
    {
        private static object LOCK = new object(); // SYNC-ROOT

        #region Fields
        private Thread _toolbarThread;
        private EditWindow _editWindow;
        private bool _toolbarRunning;
        private IModeler _modeler;

        private bool _modelerFullscreen;
        private Point _modelerLocation;
        private Size _modelerSize;
        private SceneObject _selected;

        private Queue _queue;
        #endregion // Fields


        #region Properties
        /// <summary>
        /// Queue mit zu erledigenden Events für den Modeler
        /// </summary>
        public Queue Queue { get { return _queue; } }
        #endregion // Properties


        #region Constructors
        /// <summary>
        /// Konstruktor für ToolBar
        /// </summary>
        /// <param name="modeler">Modeler, welcher dieser ToolBar zugeordnert wird</param>
        public Toolbar(IModeler modeler)
        {
            Queue q = new Queue();
            _queue = Queue.Synchronized(q);
            this._modeler = modeler;
            ShowToolbar();
        }
        #endregion // Constructors


        #region Methods
        #region Manipulation-Methods
        /// <summary>
        /// Hebt die Selektierung auf
        /// </summary>
        public void Unselect() { this.Unselect(Thread.CurrentThread == this._toolbarThread); }
        /// <summary>
        /// Hebt die Selektierung auf
        /// </summary>
        /// <param name="storeInQueue">True, wenn das Event in der Queue gespeichert werden soll</param>
        public void Unselect(bool storeInQueue)
        {
            if (storeInQueue)
                this._queue.Enqueue(new UnselectEventArgs(this._editWindow));
            else if (this._editWindow != null)
            {
                if (this._editWindow.InvokeRequired)
                    this._editWindow.Invoke(new Action(this._editWindow.Unselect));
                this._editWindow.Unselect();
            }
        }

        /// <summary>
        /// Setzt die Selektierung auf das angegebene Objekt
        /// </summary>
        /// <param name="so">Objekt, welches selektiert ist</param>
        public void Select(SceneObject so) { this.Select(so, Thread.CurrentThread == this._toolbarThread); }
        /// <summary>
        /// Setzt die Selektierung auf das angegebene Objekt
        /// </summary>
        /// <param name="so">Objekt, welches selektiert ist</param>
        /// <param name="storeInQueue">True, wenn das Event in der Queue gespeichert werden soll</param>
        public void Select(SceneObject so, bool storeInQueue)
        {
            if (storeInQueue)
                this._queue.Enqueue(new SelectEventArgs(this._editWindow, so));
            else if(this._editWindow != null)
            {
                if (this._editWindow.InvokeRequired)
                    this._editWindow.Invoke(new Action<SceneObject>(this._editWindow.Select), so);
                this._editWindow.Select(so);
            }
        }

        /// <summary>
        /// Löscht das Objekt mit der angegebenen ID
        /// </summary>
        /// <param name="id">ID des zu löschenden Objektes</param>
        public void Delete(ID id) { this.Delete(id, Thread.CurrentThread == this._toolbarThread); }
        /// <summary>
        /// Löscht das Objekt mit der angegebenen ID
        /// </summary>
        /// <param name="id">ID des zu löschenden Objektes</param>
        /// <param name="storeInQueue">True, wenn das Event in der Queue gespeichert werden soll</param>
        public void Delete(ID id, bool storeInQueue)
        {
            if (storeInQueue)
                this._queue.Enqueue(new DeleteEventArgs(this._editWindow, id));
            else if (this._editWindow != null)
            {
                if (this._editWindow.InvokeRequired)
                    this._editWindow.Invoke(new Action<ID>(this._editWindow.Delete), id);
                this._editWindow.Delete(id);
            }
        }

        /// <summary>
        /// Fordert das Einfügen eines neuen Objektes des übergebenen Types an
        /// </summary>
        /// <param name="t">Typ des neu zu erstellenden Objektes</param>
        public void Add(Type t) { this.Add(t, true); }
        /// <summary>
        /// Fügt ein neues Objekt hinzu
        /// </summary>
        /// <param name="o">Typ des Objektes, oder das Objekt selbst</param>
        /// <param name="storeInQueue">True, wenn das Event in der Queue gespeichert werden soll</param>
        public void Add(object o, bool storeInQueue)
        {
            if (storeInQueue)
                this._queue.Enqueue(new AddEventArgs(this._editWindow, o as Type));
            else if (this._editWindow != null)
            {
                if (this._editWindow.InvokeRequired)
                    this._editWindow.Invoke(new Action<SceneObject>(this._editWindow.Add), o);
                this._editWindow.Add(o as SceneObject);
            }
        }

        /// <summary>
        /// Setzt Änderungen für das übergebene Objekt
        /// </summary>
        /// <param name="so">Objekt, welches geändert wurde</param>
        public void Edit(SceneObject so) { this.Edit(so, Thread.CurrentThread == this._toolbarThread); }
        /// <summary>
        /// Setzt Änderungen für das übergebene Objekt
        /// </summary>
        /// <param name="so">Objekt, welches geändert wurde</param>
        /// <param name="storeInQueue">True, wenn das Event in der Queue gespeichert werden soll</param>
        public void Edit(SceneObject so, bool storeInQueue)
        {
            if (storeInQueue)
                this._queue.Enqueue(new EditEventArgs(this._editWindow, so));
            else if (this._editWindow != null)
            {
                if (this._editWindow.InvokeRequired)
                    this._editWindow.Invoke(new Action<SceneObject>(this._editWindow.Edit), so);
                this._editWindow.Edit(so);
            }
        }
        #endregion // Manipulation-Methods


        #region Toolbar-Thread-Methods
        /// <summary>
        /// Startet den ToolBar-Thread
        /// </summary>
        public void ShowToolbar()
        {
            lock (LOCK)
            {
                if (!_toolbarRunning)
                {
                    _modelerFullscreen = ModelerWindow.Instance.WindowState == OpenTK.WindowState.Fullscreen 
                        || ModelerWindow.Instance.WindowState == OpenTK.WindowState.Maximized;
                    this._selected = this._modeler.SelectedObject;
                    if (!_modelerFullscreen)
                    {
                        this._modelerLocation = ModelerWindow.Instance.Location;
                        this._modelerSize = ModelerWindow.Instance.Size;
                    }

                    this._toolbarThread = new Thread(new ThreadStart(Run));
                    this._toolbarThread.Name = "Toolbar-Thread";
                    this._toolbarThread.SetApartmentState(ApartmentState.STA);
                    this._toolbarThread.Start();
                }
            }
        }

        /// <summary>
        /// Run-Methode des ToolBar-Threads
        /// </summary>
        [STAThread]
        private void Run()
        {
            lock(LOCK)
            {
                this._toolbarRunning = true;
                _editWindow = new EditWindow(this, this._selected);
                SetToolbarPosition();
            }
            try
            {
                Application.Run(_editWindow);
            }
            catch (ThreadAbortException)
            {
                lock (LOCK)
                {
                    if (this._editWindow != null)
                    {
                        this._editWindow.CloseWindow();
                        this._editWindow = null;
                    }
                }
            }
            finally
            {
                _toolbarRunning = false;
            }
        }

        /// <summary>
        /// Setzt die Startposition der Toolbar
        /// </summary>
        private void SetToolbarPosition()
        {
            Point pos = new Point(50, 50);
            int width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
            int height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
            Point mouse = System.Windows.Forms.Control.MousePosition;
            if (!_modelerFullscreen)
            {// Wenn nicht FullScreen oder maximiert, wird berechnet ob links oder rechts platzt ist für die Toolbar
                int mdL = mouse.X - _modelerLocation.X;
                int mdR = (_modelerLocation.X + _modelerSize.Width) - mouse.X;
                bool left = Math.Min(mdL, mdR) == mdL;
                int posX;

                if (left)
                {
                    posX = _modelerLocation.X - _editWindow.Size.Width;
                    if (posX < 0)
                    {
                        posX = _modelerLocation.X + _modelerSize.Width;
                        if (posX + _editWindow.Size.Width > width)
                            _modelerFullscreen = true;
                    }
                }
                else
                {
                    posX = _modelerLocation.X + _modelerSize.Width;
                    if (posX + _editWindow.Size.Width > width)
                    {
                        posX = _modelerLocation.X - _editWindow.Size.Width;
                        if (posX < 0)
                            _modelerFullscreen = true;
                    }
                }
                if (!_modelerFullscreen)
                    pos = new Point(posX, _modelerLocation.Y);
            }

            if (_modelerFullscreen)
            { // Wenn kein Platz für die Toolbar neben dem Fenster ist, wir sie an der Stelle des Mauszeigers geöffnet
                pos = mouse;
                pos.X -= 10;
                pos.Y -= 10;

                if (pos.X + _editWindow.Size.Width > width)
                    pos.X = width - _editWindow.Size.Width;
                if (pos.Y + _editWindow.Size.Height > height)
                    pos.Y = height - _editWindow.Size.Height;
            }
            this._editWindow.SetDesktopLocation(pos.X, pos.Y);
        }

        /// <summary>
        /// Gibt die Ressourcen des Objektes frei
        /// </summary>
        public void Dispose() { this.Close(); }
        /// <summary>
        /// Terminiert den ToolBar-Thread
        /// </summary>
        public void Close()
        {
            if (this._toolbarRunning && this._toolbarThread != null)
            {
                this._toolbarThread.Abort(); // Toolbar-Thread beenden
                this._toolbarThread.Join();  // Auf die Terminierung des Threads warten
            }
        }
        #endregion // Toolbar-Thread-Methods
        #endregion // Methods
    }
}
